home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / domacnost a kancelar / joomla / Joomla_1.5.4-Stable-Full_Package.exe / libraries / phpxmlrpc / xmlrpcs.php < prev   
PHP Script  |  2008-07-06  |  38KB  |  1,184 lines

  1. <?php
  2. // by Edd Dumbill (C) 1999-2002
  3. // <edd@usefulinc.com>
  4. // $Id: xmlrpcs.inc,v 1.67 2007/05/22 21:31:58 ggiunta Exp $
  5.  
  6. // Copyright (c) 1999,2000,2002 Edd Dumbill.
  7. // All rights reserved.
  8. //
  9. // Redistribution and use in source and binary forms, with or without
  10. // modification, are permitted provided that the following conditions
  11. // are met:
  12. //
  13. //    * Redistributions of source code must retain the above copyright
  14. //      notice, this list of conditions and the following disclaimer.
  15. //
  16. //    * Redistributions in binary form must reproduce the above
  17. //      copyright notice, this list of conditions and the following
  18. //      disclaimer in the documentation and/or other materials provided
  19. //      with the distribution.
  20. //
  21. //    * Neither the name of the "XML-RPC for PHP" nor the names of its
  22. //      contributors may be used to endorse or promote products derived
  23. //      from this software without specific prior written permission.
  24. //
  25. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  28. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  29. // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  30. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  34. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  36. // OF THE POSSIBILITY OF SUCH DAMAGE.
  37.  
  38.     // XML RPC Server class
  39.     // requires: xmlrpc.inc
  40.  
  41.     $GLOBALS['xmlrpcs_capabilities'] = array(
  42.         // xmlrpc spec: always supported
  43.         'xmlrpc' => new xmlrpcval(array(
  44.             'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
  45.             'specVersion' => new xmlrpcval(1, 'int')
  46.         ), 'struct'),
  47.         // if we support system.xxx functions, we always support multicall, too...
  48.         // Note that, as of 2006/09/17, the following URL does not respond anymore
  49.         'system.multicall' => new xmlrpcval(array(
  50.             'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
  51.             'specVersion' => new xmlrpcval(1, 'int')
  52.         ), 'struct'),
  53.         // introspection: version 2! we support 'mixed', too
  54.         'introspection' => new xmlrpcval(array(
  55.             'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
  56.             'specVersion' => new xmlrpcval(2, 'int')
  57.         ), 'struct')
  58.     );
  59.  
  60.     /* Functions that implement system.XXX methods of xmlrpc servers */
  61.     $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
  62.     $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
  63.     $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
  64.     function _xmlrpcs_getCapabilities($server, $m=null)
  65.     {
  66.         $outAr = $GLOBALS['xmlrpcs_capabilities'];
  67.         // NIL extension
  68.         if ($GLOBALS['xmlrpc_null_extension']) {
  69.             $outAr['nil'] = new xmlrpcval(array(
  70.                 'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
  71.                 'specVersion' => new xmlrpcval(1, 'int')
  72.             ), 'struct');
  73.         }
  74.         return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
  75.     }
  76.  
  77.     // listMethods: signature was either a string, or nothing.
  78.     // The useless string variant has been removed
  79.     $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
  80.     $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
  81.     $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
  82.     function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
  83.     {
  84.  
  85.         $outAr=array();
  86.         foreach($server->dmap as $key => $val)
  87.         {
  88.             $outAr[]=&new xmlrpcval($key, 'string');
  89.         }
  90.         if($server->allow_system_funcs)
  91.         {
  92.             foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
  93.             {
  94.                 $outAr[]=&new xmlrpcval($key, 'string');
  95.             }
  96.         }
  97.         return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
  98.     }
  99.  
  100.     $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
  101.     $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
  102.     $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
  103.     function _xmlrpcs_methodSignature($server, $m)
  104.     {
  105.         // let accept as parameter both an xmlrpcval or string
  106.         if (is_object($m))
  107.         {
  108.             $methName=$m->getParam(0);
  109.             $methName=$methName->scalarval();
  110.         }
  111.         else
  112.         {
  113.             $methName=$m;
  114.         }
  115.         if(strpos($methName, "system.") === 0)
  116.         {
  117.             $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
  118.         }
  119.         else
  120.         {
  121.             $dmap=$server->dmap; $sysCall=0;
  122.         }
  123.         if(isset($dmap[$methName]))
  124.         {
  125.             if(isset($dmap[$methName]['signature']))
  126.             {
  127.                 $sigs=array();
  128.                 foreach($dmap[$methName]['signature'] as $inSig)
  129.                 {
  130.                     $cursig=array();
  131.                     foreach($inSig as $sig)
  132.                     {
  133.                         $cursig[]=&new xmlrpcval($sig, 'string');
  134.                     }
  135.                     $sigs[]=&new xmlrpcval($cursig, 'array');
  136.                 }
  137.                 $r=&new xmlrpcresp(new xmlrpcval($sigs, 'array'));
  138.             }
  139.             else
  140.             {
  141.                 // NB: according to the official docs, we should be returning a
  142.                 // "none-array" here, which means not-an-array
  143.                 $r=&new xmlrpcresp(new xmlrpcval('undef', 'string'));
  144.             }
  145.         }
  146.         else
  147.         {
  148.             $r=&new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
  149.         }
  150.         return $r;
  151.     }
  152.  
  153.     $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
  154.     $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
  155.     $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
  156.     function _xmlrpcs_methodHelp($server, $m)
  157.     {
  158.         // let accept as parameter both an xmlrpcval or string
  159.         if (is_object($m))
  160.         {
  161.             $methName=$m->getParam(0);
  162.             $methName=$methName->scalarval();
  163.         }
  164.         else
  165.         {
  166.             $methName=$m;
  167.         }
  168.         if(strpos($methName, "system.") === 0)
  169.         {
  170.             $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
  171.         }
  172.         else
  173.         {
  174.             $dmap=$server->dmap; $sysCall=0;
  175.         }
  176.         if(isset($dmap[$methName]))
  177.         {
  178.             if(isset($dmap[$methName]['docstring']))
  179.             {
  180.                 $r=&new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
  181.             }
  182.             else
  183.             {
  184.                 $r=&new xmlrpcresp(new xmlrpcval('', 'string'));
  185.             }
  186.         }
  187.         else
  188.         {
  189.             $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
  190.         }
  191.         return $r;
  192.     }
  193.  
  194.     $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
  195.     $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
  196.     $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
  197.     function _xmlrpcs_multicall_error($err)
  198.     {
  199.         if(is_string($err))
  200.         {
  201.             $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
  202.             $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
  203.         }
  204.         else
  205.         {
  206.             $code = $err->faultCode();
  207.             $str = $err->faultString();
  208.         }
  209.         $struct = array();
  210.         $struct['faultCode'] =& new xmlrpcval($code, 'int');
  211.         $struct['faultString'] =& new xmlrpcval($str, 'string');
  212.         return new xmlrpcval($struct, 'struct');
  213.     }
  214.  
  215.     function _xmlrpcs_multicall_do_call($server, $call)
  216.     {
  217.         if($call->kindOf() != 'struct')
  218.         {
  219.             return _xmlrpcs_multicall_error('notstruct');
  220.         }
  221.         $methName = @$call->structmem('methodName');
  222.         if(!$methName)
  223.         {
  224.             return _xmlrpcs_multicall_error('nomethod');
  225.         }
  226.         if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
  227.         {
  228.             return _xmlrpcs_multicall_error('notstring');
  229.         }
  230.         if($methName->scalarval() == 'system.multicall')
  231.         {
  232.             return _xmlrpcs_multicall_error('recursion');
  233.         }
  234.  
  235.         $params = @$call->structmem('params');
  236.         if(!$params)
  237.         {
  238.             return _xmlrpcs_multicall_error('noparams');
  239.         }
  240.         if($params->kindOf() != 'array')
  241.         {
  242.             return _xmlrpcs_multicall_error('notarray');
  243.         }
  244.         $numParams = $params->arraysize();
  245.  
  246.         $msg =& new xmlrpcmsg($methName->scalarval());
  247.         for($i = 0; $i < $numParams; $i++)
  248.         {
  249.             if(!$msg->addParam($params->arraymem($i)))
  250.             {
  251.                 $i++;
  252.                 return _xmlrpcs_multicall_error(new xmlrpcresp(0,
  253.                     $GLOBALS['xmlrpcerr']['incorrect_params'],
  254.                     $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
  255.             }
  256.         }
  257.  
  258.         $result = $server->execute($msg);
  259.  
  260.         if($result->faultCode() != 0)
  261.         {
  262.             return _xmlrpcs_multicall_error($result);        // Method returned fault.
  263.         }
  264.  
  265.         return new xmlrpcval(array($result->value()), 'array');
  266.     }
  267.  
  268.     function _xmlrpcs_multicall_do_call_phpvals($server, $call)
  269.     {
  270.         if(!is_array($call))
  271.         {
  272.             return _xmlrpcs_multicall_error('notstruct');
  273.         }
  274.         if(!array_key_exists('methodName', $call))
  275.         {
  276.             return _xmlrpcs_multicall_error('nomethod');
  277.         }
  278.         if (!is_string($call['methodName']))
  279.         {
  280.             return _xmlrpcs_multicall_error('notstring');
  281.         }
  282.         if($call['methodName'] == 'system.multicall')
  283.         {
  284.             return _xmlrpcs_multicall_error('recursion');
  285.         }
  286.         if(!array_key_exists('params', $call))
  287.         {
  288.             return _xmlrpcs_multicall_error('noparams');
  289.         }
  290.         if(!is_array($call['params']))
  291.         {
  292.             return _xmlrpcs_multicall_error('notarray');
  293.         }
  294.  
  295.         // this is a real dirty and simplistic hack, since we might have received a
  296.         // base64 or datetime values, but they will be listed as strings here...
  297.         $numParams = count($call['params']);
  298.         $pt = array();
  299.         foreach($call['params'] as $val)
  300.             $pt[] = php_2_xmlrpc_type(gettype($val));
  301.  
  302.         $result = $server->execute($call['methodName'], $call['params'], $pt);
  303.  
  304.         if($result->faultCode() != 0)
  305.         {
  306.             return _xmlrpcs_multicall_error($result);        // Method returned fault.
  307.         }
  308.  
  309.         return new xmlrpcval(array($result->value()), 'array');
  310.     }
  311.  
  312.     function _xmlrpcs_multicall($server, $m)
  313.     {
  314.         $result = array();
  315.         // let accept a plain list of php parameters, beside a single xmlrpc msg object
  316.         if (is_object($m))
  317.         {
  318.             $calls = $m->getParam(0);
  319.             $numCalls = $calls->arraysize();
  320.             for($i = 0; $i < $numCalls; $i++)
  321.             {
  322.                 $call = $calls->arraymem($i);
  323.                 $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
  324.             }
  325.         }
  326.         else
  327.         {
  328.             $numCalls=count($m);
  329.             for($i = 0; $i < $numCalls; $i++)
  330.             {
  331.                 $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
  332.             }
  333.         }
  334.  
  335.         return new xmlrpcresp(new xmlrpcval($result, 'array'));
  336.     }
  337.  
  338.     $GLOBALS['_xmlrpcs_dmap']=array(
  339.         'system.listMethods' => array(
  340.             'function' => '_xmlrpcs_listMethods',
  341.             'signature' => $_xmlrpcs_listMethods_sig,
  342.             'docstring' => $_xmlrpcs_listMethods_doc,
  343.             'signature_docs' => $_xmlrpcs_listMethods_sdoc),
  344.         'system.methodHelp' => array(
  345.             'function' => '_xmlrpcs_methodHelp',
  346.             'signature' => $_xmlrpcs_methodHelp_sig,
  347.             'docstring' => $_xmlrpcs_methodHelp_doc,
  348.             'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
  349.         'system.methodSignature' => array(
  350.             'function' => '_xmlrpcs_methodSignature',
  351.             'signature' => $_xmlrpcs_methodSignature_sig,
  352.             'docstring' => $_xmlrpcs_methodSignature_doc,
  353.             'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
  354.         'system.multicall' => array(
  355.             'function' => '_xmlrpcs_multicall',
  356.             'signature' => $_xmlrpcs_multicall_sig,
  357.             'docstring' => $_xmlrpcs_multicall_doc,
  358.             'signature_docs' => $_xmlrpcs_multicall_sdoc),
  359.         'system.getCapabilities' => array(
  360.             'function' => '_xmlrpcs_getCapabilities',
  361.             'signature' => $_xmlrpcs_getCapabilities_sig,
  362.             'docstring' => $_xmlrpcs_getCapabilities_doc,
  363.             'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
  364.     );
  365.  
  366.     $GLOBALS['_xmlrpcs_occurred_errors'] = '';
  367.     $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
  368.     /**
  369.     * Error handler used to track errors that occur during server-side execution of PHP code.
  370.     * This allows to report back to the client whether an internal error has occurred or not
  371.     * using an xmlrpc response object, instead of letting the client deal with the html junk
  372.     * that a PHP execution error on the server generally entails.
  373.     *
  374.     * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
  375.     *
  376.     */
  377.     function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
  378.     {
  379.         // obey the @ protocol
  380.         if (error_reporting() == 0)
  381.             return;
  382.  
  383.         //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
  384.         if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined
  385.         {
  386.             $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
  387.         }
  388.         // Try to avoid as much as possible disruption to the previous error handling
  389.         // mechanism in place
  390.         if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
  391.         {
  392.             // The previous error handler was the default: all we should do is log error
  393.             // to the default error log (if level high enough)
  394.             if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
  395.             {
  396.                 error_log($errstring);
  397.             }
  398.         }
  399.         else
  400.         {
  401.             // Pass control on to previous error handler, trying to avoid loops...
  402.             if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
  403.             {
  404.                 // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
  405.                 if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
  406.                 {
  407.                     $GLOBALS['_xmlrpcs_prev_ehandler'][0]->$GLOBALS['_xmlrpcs_prev_ehandler'][1]($errcode, $errstring, $filename, $lineno, $context);
  408.                 }
  409.                 else
  410.                 {
  411.                     $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
  412.                 }
  413.             }
  414.         }
  415.     }
  416.  
  417.     $GLOBALS['_xmlrpc_debuginfo']='';
  418.  
  419.     /**
  420.     * Add a string to the debug info that can be later seralized by the server
  421.     * as part of the response message.
  422.     * Note that for best compatbility, the debug string should be encoded using
  423.     * the $GLOBALS['xmlrpc_internalencoding'] character set.
  424.     * @param string $m
  425.     * @access public
  426.     */
  427.     function xmlrpc_debugmsg($m)
  428.     {
  429.         $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
  430.     }
  431.  
  432.     class xmlrpc_server
  433.     {
  434.         /// array defining php functions exposed as xmlrpc methods by this server
  435.         var $dmap=array();
  436.         /**
  437.         * Defines how functions in dmap will be invokde: either using an xmlrpc msg object
  438.         * or plain php values.
  439.         * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
  440.         */
  441.         var $functions_parameters_type='xmlrpcvals';
  442.         /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
  443.         var $debug = 1;
  444.         /**
  445.         * When set to true, it will enable HTTP compression of the response, in case
  446.         * the client has declared its support for compression in the request.
  447.         */
  448.         var $compress_response = false;
  449.         /**
  450.         * List of http compression methods accepted by the server for requests.
  451.         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
  452.         */
  453.         var $accepted_compression = array();
  454.         /// shall we serve calls to system.* methods?
  455.         var $allow_system_funcs = true;
  456.         /// list of charset encodings natively accepted for requests
  457.         var $accepted_charset_encodings = array();
  458.         /**
  459.         * charset encoding to be used for response.
  460.         * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
  461.         * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
  462.         * null (leave unspecified in response, convert output stream to US_ASCII),
  463.         * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
  464.         * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
  465.         * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
  466.         */
  467.         var $response_charset_encoding = '';
  468.         /// storage for internal debug info
  469.         var $debug_info = '';
  470.         /// extra data passed at runtime to method handling functions. Used only by EPI layer
  471.         var $user_data = null;
  472.  
  473.         /**
  474.         * @param array $dispmap the dispatch map withd efinition of exposed services
  475.         * @param boolean $servicenow set to false to prevent the server from runnung upon construction
  476.         */
  477.         function xmlrpc_server($dispMap=null, $serviceNow=true)
  478.         {
  479.             // if ZLIB is enabled, let the server by default accept compressed requests,
  480.             // and compress responses sent to clients that support them
  481.             if(function_exists('gzinflate'))
  482.             {
  483.                 $this->accepted_compression = array('gzip', 'deflate');
  484.                 $this->compress_response = true;
  485.             }
  486.  
  487.             // by default the xml parser can support these 3 charset encodings
  488.             $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
  489.  
  490.             // dispMap is a dispatch array of methods
  491.             // mapped to function names and signatures
  492.             // if a method
  493.             // doesn't appear in the map then an unknown
  494.             // method error is generated
  495.             /* milosch - changed to make passing dispMap optional.
  496.              * instead, you can use the class add_to_map() function
  497.              * to add functions manually (borrowed from SOAPX4)
  498.              */
  499.             if($dispMap)
  500.             {
  501.                 $this->dmap = $dispMap;
  502.                 if($serviceNow)
  503.                 {
  504.                     $this->service();
  505.                 }
  506.             }
  507.         }
  508.  
  509.         /**
  510.         * Set debug level of server.
  511.         * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
  512.         * 0 = no debug info,
  513.         * 1 = msgs set from user with debugmsg(),
  514.         * 2 = add complete xmlrpc request (headers and body),
  515.         * 3 = add also all processing warnings happened during method processing
  516.         * (NB: this involves setting a custom error handler, and might interfere
  517.         * with the standard processing of the php function exposed as method. In
  518.         * particular, triggering an USER_ERROR level error will not halt script
  519.         * execution anymore, but just end up logged in the xmlrpc response)
  520.         * Note that info added at elevel 2 and 3 will be base64 encoded
  521.         * @access public
  522.         */
  523.         function setDebug($in)
  524.         {
  525.             $this->debug=$in;
  526.         }
  527.  
  528.         /**
  529.         * Return a string with the serialized representation of all debug info
  530.         * @param string $charset_encoding the target charset encoding for the serialization
  531.         * @return string an XML comment (or two)
  532.         */
  533.         function serializeDebug($charset_encoding='')
  534.         {
  535.             // Tough encoding problem: which internal charset should we assume for debug info?
  536.             // It might contain a copy of raw data received from client, ie with unknown encoding,
  537.             // intermixed with php generated data and user generated data...
  538.             // so we split it: system debug is base 64 encoded,
  539.             // user debug info should be encoded by the end user using the INTERNAL_ENCODING
  540.             $out = '';
  541.             if ($this->debug_info != '')
  542.             {
  543.                 $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
  544.             }
  545.             if($GLOBALS['_xmlrpc_debuginfo']!='')
  546.             {
  547.  
  548.                 $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
  549.                 // NB: a better solution MIGHT be to use CDATA, but we need to insert it
  550.                 // into return payload AFTER the beginning tag
  551.                 //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
  552.             }
  553.             return $out;
  554.         }
  555.  
  556.         /**
  557.         * Execute the xmlrpc request, printing the response
  558.         * @param string $data the request body. If null, the http POST request will be examined
  559.         * @return xmlrpcresp the response object (usually not used by caller...)
  560.         * @access public
  561.         */
  562.         function service($data=null, $return_payload=false)
  563.         {
  564.             if ($data === null)
  565.             {
  566.                 // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
  567.                 $ver = phpversion();
  568.                 if ($ver[0] >= 5)
  569.                 {
  570.                     $data = file_get_contents('php://input');
  571.                 }
  572.                 else
  573.                 {
  574.                     // Check if it has a value, if it doesn't have a value try and read php://input but supress the error
  575.                     // this will mimic returning an empty string, without a "cant find wrapper error" and allow backwards compat
  576.                     // php docs are unclear as to when this was added, works on php 4.4 at least, and probably 4.3
  577.                     $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : @file_get_contents('php://input');
  578.                 }
  579.             }
  580.             $raw_data = $data;
  581.  
  582.             // reset internal debug info
  583.             $this->debug_info = '';
  584.  
  585.             // Echo back what we received, before parsing it
  586.             if($this->debug > 1)
  587.             {
  588.                 $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
  589.             }
  590.  
  591.             $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
  592.             if (!$r)
  593.             {
  594.                 $r=$this->parseRequest($data, $req_charset);
  595.             }
  596.  
  597.             // save full body of request into response, for more debugging usages
  598.             $r->raw_data = $raw_data;
  599.  
  600.             if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
  601.             {
  602.                 $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
  603.                     $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
  604.             }
  605.  
  606.             $payload=$this->xml_header($resp_charset);
  607.             if($this->debug > 0)
  608.             {
  609.                 $payload = $payload . $this->serializeDebug($resp_charset);
  610.             }
  611.  
  612.             // G. Giunta 2006-01-27: do not create response serialization if it has
  613.             // already happened. Helps building json magic
  614.             if (empty($r->payload))
  615.             {
  616.                 $r->serialize($resp_charset);
  617.             }
  618.             $payload = $payload . $r->payload;
  619.  
  620.             if ($return_payload)
  621.             {
  622.                 return $payload;
  623.             }
  624.  
  625.             // if we get a warning/error that has output some text before here, then we cannot
  626.             // add a new header. We cannot say we are sending xml, either...
  627.             if(!headers_sent())
  628.             {
  629.                 header('Content-Type: '.$r->content_type);
  630.                 // we do not know if client actually told us an accepted charset, but if he did
  631.                 // we have to tell him what we did
  632.                 header("Vary: Accept-Charset");
  633.  
  634.                 // http compression of output: only
  635.                 // if we can do it, and we want to do it, and client asked us to,
  636.                 // and php ini settings do not force it already
  637.                 $php_no_self_compress = ini_get('zlib.output_compression') == '' && (ini_get('output_handler') != 'ob_gzhandler');
  638.                 if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
  639.                     && $php_no_self_compress)
  640.                 {
  641.                     if(strpos($resp_encoding, 'gzip') !== false)
  642.                     {
  643.                         $payload = gzencode($payload);
  644.                         header("Content-Encoding: gzip");
  645.                         header("Vary: Accept-Encoding");
  646.                     }
  647.                     elseif (strpos($resp_encoding, 'deflate') !== false)
  648.                     {
  649.                         $payload = gzcompress($payload);
  650.                         header("Content-Encoding: deflate");
  651.                         header("Vary: Accept-Encoding");
  652.                     }
  653.                 }
  654.  
  655.                 // do not ouput content-length header if php is compressing output for us:
  656.                 // it will mess up measurements
  657.                 if($php_no_self_compress)
  658.                 {
  659.                     header('Content-Length: ' . (int)strlen($payload));
  660.                 }
  661.             }
  662.             else
  663.             {
  664.                 error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages');
  665.             }
  666.  
  667.             print $payload;
  668.  
  669.             // return request, in case subclasses want it
  670.             return $r;
  671.         }
  672.  
  673.         /**
  674.         * Add a method to the dispatch map
  675.         * @param string $methodname the name with which the method will be made available
  676.         * @param string $function the php function that will get invoked
  677.         * @param array $sig the array of valid method signatures
  678.         * @param string $doc method documentation
  679.         * @access public
  680.         */
  681.         function add_to_map($methodname,$function,$sig=null,$doc='')
  682.         {
  683.             $this->dmap[$methodname] = array(
  684.                 'function'    => $function,
  685.                 'docstring' => $doc
  686.             );
  687.             if ($sig)
  688.             {
  689.                 $this->dmap[$methodname]['signature'] = $sig;
  690.             }
  691.         }
  692.  
  693.         /**
  694.         * Verify type and number of parameters received against a list of known signatures
  695.         * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
  696.         * @param array $sig array of known signatures to match against
  697.         * @access private
  698.         */
  699.         function verifySignature($in, $sig)
  700.         {
  701.             // check each possible signature in turn
  702.             if (is_object($in))
  703.             {
  704.                 $numParams = $in->getNumParams();
  705.             }
  706.             else
  707.             {
  708.                 $numParams = count($in);
  709.             }
  710.             foreach($sig as $cursig)
  711.             {
  712.                 if(count($cursig)==$numParams+1)
  713.                 {
  714.                     $itsOK=1;
  715.                     for($n=0; $n<$numParams; $n++)
  716.                     {
  717.                         if (is_object($in))
  718.                         {
  719.                             $p=$in->getParam($n);
  720.                             if($p->kindOf() == 'scalar')
  721.                             {
  722.                                 $pt=$p->scalartyp();
  723.                             }
  724.                             else
  725.                             {
  726.                                 $pt=$p->kindOf();
  727.                             }
  728.                         }
  729.                         else
  730.                         {
  731.                             $pt= $in[$n] == 'i4' ? 'int' : $in[$n]; // dispatch maps never use i4...
  732.                         }
  733.  
  734.                         // param index is $n+1, as first member of sig is return type
  735.                         if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
  736.                         {
  737.                             $itsOK=0;
  738.                             $pno=$n+1;
  739.                             $wanted=$cursig[$n+1];
  740.                             $got=$pt;
  741.                             break;
  742.                         }
  743.                     }
  744.                     if($itsOK)
  745.                     {
  746.                         return array(1,'');
  747.                     }
  748.                 }
  749.             }
  750.             if(isset($wanted))
  751.             {
  752.                 return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
  753.             }
  754.             else
  755.             {
  756.                 return array(0, "No method signature matches number of parameters");
  757.             }
  758.         }
  759.  
  760.         /**
  761.         * Parse http headers received along with xmlrpc request. If needed, inflate request
  762.         * @return null on success or an xmlrpcresp
  763.         * @access private
  764.         */
  765.         function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
  766.         {
  767.             // Play nice to PHP 4.0.x: superglobals were not yet invented...
  768.             if(!isset($_SERVER))
  769.             {
  770.                 $_SERVER = $GLOBALS['HTTP_SERVER_VARS'];
  771.             }
  772.  
  773.             if($this->debug > 1)
  774.             {
  775.                 if(function_exists('getallheaders'))
  776.                 {
  777.                     $this->debugmsg(''); // empty line
  778.                     foreach(getallheaders() as $name => $val)
  779.                     {
  780.                         $this->debugmsg("HEADER: $name: $val");
  781.                     }
  782.                 }
  783.  
  784.             }
  785.  
  786.             if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
  787.             {
  788.                 $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
  789.             }
  790.             else
  791.             {
  792.                 $content_encoding = '';
  793.             }
  794.  
  795.             // check if request body has been compressed and decompress it
  796.             if($content_encoding != '' && strlen($data))
  797.             {
  798.                 if($content_encoding == 'deflate' || $content_encoding == 'gzip')
  799.                 {
  800.                     // if decoding works, use it. else assume data wasn't gzencoded
  801.                     if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
  802.                     {
  803.                         if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
  804.                         {
  805.                             $data = $degzdata;
  806.                             if($this->debug > 1)
  807.                             {
  808.                                 $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
  809.                             }
  810.                         }
  811.                         elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
  812.                         {
  813.                             $data = $degzdata;
  814.                             if($this->debug > 1)
  815.                                 $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
  816.                         }
  817.                         else
  818.                         {
  819.                             $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
  820.                             return $r;
  821.                         }
  822.                     }
  823.                     else
  824.                     {
  825.                         //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
  826.                         $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
  827.                         return $r;
  828.                     }
  829.                 }
  830.             }
  831.  
  832.             // check if client specified accepted charsets, and if we know how to fulfill
  833.             // the request
  834.             if ($this->response_charset_encoding == 'auto')
  835.             {
  836.                 $resp_encoding = '';
  837.                 if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
  838.                 {
  839.                     // here we should check if we can match the client-requested encoding
  840.                     // with the encodings we know we can generate.
  841.                     /// @todo we should parse q=0.x preferences instead of getting first charset specified...
  842.                     $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
  843.                     // Give preference to internal encoding
  844.                     $known_charsets = array($this->internal_encoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII');
  845.                     foreach ($known_charsets as $charset)
  846.                     {
  847.                         foreach ($client_accepted_charsets as $accepted)
  848.                             if (strpos($accepted, $charset) === 0)
  849.                             {
  850.                                 $resp_encoding = $charset;
  851.                                 break;
  852.                             }
  853.                         if ($resp_encoding)
  854.                             break;
  855.                     }
  856.                 }
  857.             }
  858.             else
  859.             {
  860.                 $resp_encoding = $this->response_charset_encoding;
  861.             }
  862.  
  863.             if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
  864.             {
  865.                 $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
  866.             }
  867.             else
  868.             {
  869.                 $resp_compression = '';
  870.             }
  871.  
  872.             // 'guestimate' request encoding
  873.             /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
  874.             $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
  875.                 $data);
  876.  
  877.             return null;
  878.         }
  879.  
  880.         /**
  881.         * Parse an xml chunk containing an xmlrpc request and execute the corresponding
  882.         * php function registered with the server
  883.         * @param string $data the xml request
  884.         * @param string $req_encoding (optional) the charset encoding of the xml request
  885.         * @return xmlrpcresp
  886.         * @access private
  887.         */
  888.         function parseRequest($data, $req_encoding='')
  889.         {
  890.             // 2005/05/07 commented and moved into caller function code
  891.             //if($data=='')
  892.             //{
  893.             //    $data=$GLOBALS['HTTP_RAW_POST_DATA'];
  894.             //}
  895.  
  896.             // G. Giunta 2005/02/13: we do NOT expect to receive html entities
  897.             // so we do not try to convert them into xml character entities
  898.             //$data = xmlrpc_html_entity_xlate($data);
  899.  
  900.             $GLOBALS['_xh']=array();
  901.             $GLOBALS['_xh']['ac']='';
  902.             $GLOBALS['_xh']['stack']=array();
  903.             $GLOBALS['_xh']['valuestack'] = array();
  904.             $GLOBALS['_xh']['params']=array();
  905.             $GLOBALS['_xh']['pt']=array();
  906.             $GLOBALS['_xh']['isf']=0;
  907.             $GLOBALS['_xh']['isf_reason']='';
  908.             $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
  909.             $GLOBALS['_xh']['rt']='';
  910.  
  911.             // decompose incoming XML into request structure
  912.             if ($req_encoding != '')
  913.             {
  914.                 if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
  915.                 // the following code might be better for mb_string enabled installs, but
  916.                 // makes the lib about 200% slower...
  917.                 //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
  918.                 {
  919.                     error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding);
  920.                     $req_encoding = $GLOBALS['xmlrpc_defencoding'];
  921.                 }
  922.                 /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
  923.                 // the encoding is not UTF8 and there are non-ascii chars in the text...
  924.                 $parser = xml_parser_create($req_encoding);
  925.             }
  926.             else
  927.             {
  928.                 $parser = xml_parser_create();
  929.             }
  930.  
  931.             xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
  932.             // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
  933.             // the xml parser to give us back data in the expected charset
  934.             xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
  935.  
  936.             if ($this->functions_parameters_type != 'xmlrpcvals')
  937.                 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
  938.             else
  939.                 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
  940.             xml_set_character_data_handler($parser, 'xmlrpc_cd');
  941.             xml_set_default_handler($parser, 'xmlrpc_dh');
  942.             if(!xml_parse($parser, $data, 1))
  943.             {
  944.                 // return XML error as a faultCode
  945.                 $r=&new xmlrpcresp(0,
  946.                 $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
  947.                 sprintf('XML error: %s at line %d, column %d',
  948.                     xml_error_string(xml_get_error_code($parser)),
  949.                     xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
  950.                 xml_parser_free($parser);
  951.             }
  952.             elseif ($GLOBALS['_xh']['isf'])
  953.             {
  954.                 xml_parser_free($parser);
  955.                 $r=&new xmlrpcresp(0,
  956.                     $GLOBALS['xmlrpcerr']['invalid_request'],
  957.                     $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
  958.             }
  959.             else
  960.             {
  961.                 xml_parser_free($parser);
  962.                 if ($this->functions_parameters_type != 'xmlrpcvals')
  963.                 {
  964.                     if($this->debug > 1)
  965.                     {
  966.                         $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
  967.                     }
  968.                     $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
  969.                 }
  970.                 else
  971.                 {
  972.                     // build an xmlrpcmsg object with data parsed from xml
  973.                     $m=&new xmlrpcmsg($GLOBALS['_xh']['method']);
  974.                     // now add parameters in
  975.                     for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
  976.                     {
  977.                         $m->addParam($GLOBALS['_xh']['params'][$i]);
  978.                     }
  979.  
  980.                     if($this->debug > 1)
  981.                     {
  982.                         $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
  983.                     }
  984.  
  985.                     $r = $this->execute($m);
  986.                 }
  987.             }
  988.             return $r;
  989.         }
  990.  
  991.         /**
  992.         * Execute a method invoked by the client, checking parameters used
  993.         * @param mixed $m either an xmlrpcmsg obj or a method name
  994.         * @param array $params array with method parameters as php types (if m is method name only)
  995.         * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
  996.         * @return xmlrpcresp
  997.         * @access private
  998.         */
  999.         function execute($m, $params=null, $paramtypes=null)
  1000.         {
  1001.             if (is_object($m))
  1002.             {
  1003.                 $methName = $m->method();
  1004.             }
  1005.             else
  1006.             {
  1007.                 $methName = $m;
  1008.             }
  1009.             $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
  1010.             $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
  1011.  
  1012.             if(!isset($dmap[$methName]['function']))
  1013.             {
  1014.                 // No such method
  1015.                 return new xmlrpcresp(0,
  1016.                     $GLOBALS['xmlrpcerr']['unknown_method'],
  1017.                     $GLOBALS['xmlrpcstr']['unknown_method']);
  1018.             }
  1019.  
  1020.             // Check signature
  1021.             if(isset($dmap[$methName]['signature']))
  1022.             {
  1023.                 $sig = $dmap[$methName]['signature'];
  1024.                 if (is_object($m))
  1025.                 {
  1026.                     list($ok, $errstr) = $this->verifySignature($m, $sig);
  1027.                 }
  1028.                 else
  1029.                 {
  1030.                     list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
  1031.                 }
  1032.                 if(!$ok)
  1033.                 {
  1034.                     // Didn't match.
  1035.                     return new xmlrpcresp(
  1036.                         0,
  1037.                         $GLOBALS['xmlrpcerr']['incorrect_params'],
  1038.                         $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
  1039.                     );
  1040.                 }
  1041.             }
  1042.  
  1043.             $func = $dmap[$methName]['function'];
  1044.             // let the 'class::function' syntax be accepted in dispatch maps
  1045.             if(is_string($func) && strpos($func, '::'))
  1046.             {
  1047.                 $func = explode('::', $func);
  1048.             }
  1049.             // verify that function to be invoked is in fact callable
  1050.             if(!is_callable($func))
  1051.             {
  1052.                 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable");
  1053.                 return new xmlrpcresp(
  1054.                     0,
  1055.                     $GLOBALS['xmlrpcerr']['server_error'],
  1056.                     $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
  1057.                 );
  1058.             }
  1059.  
  1060.             // If debug level is 3, we should catch all errors generated during
  1061.             // processing of user function, and log them as part of response
  1062.             if($this->debug > 2)
  1063.             {
  1064.                 $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
  1065.             }
  1066.             if (is_object($m))
  1067.             {
  1068.                 if($sysCall)
  1069.                 {
  1070.                     $r = call_user_func($func, $this, $m);
  1071.                 }
  1072.                 else
  1073.                 {
  1074.                     $r = call_user_func($func, $m);
  1075.                 }
  1076.                 if (!is_a($r, 'xmlrpcresp'))
  1077.                 {
  1078.                     error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object");
  1079.                     if (is_a($r, 'xmlrpcval'))
  1080.                     {
  1081.                         $r =& new xmlrpcresp($r);
  1082.                     }
  1083.                     else
  1084.                     {
  1085.                         $r =& new xmlrpcresp(
  1086.                             0,
  1087.                             $GLOBALS['xmlrpcerr']['server_error'],
  1088.                             $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
  1089.                         );
  1090.                     }
  1091.                 }
  1092.             }
  1093.             else
  1094.             {
  1095.                 // call a 'plain php' function
  1096.                 if($sysCall)
  1097.                 {
  1098.                     array_unshift($params, $this);
  1099.                     $r = call_user_func_array($func, $params);
  1100.                 }
  1101.                 else
  1102.                 {
  1103.                     // 3rd API convention for method-handling functions: EPI-style
  1104.                     if ($this->functions_parameters_type == 'epivals')
  1105.                     {
  1106.                         $r = call_user_func_array($func, array($methName, $params, $this->user_data));
  1107.                         // mimic EPI behaviour: if we get an array that looks like an error, make it
  1108.                         // an eror response
  1109.                         if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
  1110.                         {
  1111.                             $r =& new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
  1112.                         }
  1113.                         else
  1114.                         {
  1115.                             // functions using EPI api should NOT return resp objects,
  1116.                             // so make sure we encode the return type correctly
  1117.                             $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
  1118.                         }
  1119.                     }
  1120.                     else
  1121.                     {
  1122.                         $r = call_user_func_array($func, $params);
  1123.                     }
  1124.                 }
  1125.                 // the return type can be either an xmlrpcresp object or a plain php value...
  1126.                 if (!is_a($r, 'xmlrpcresp'))
  1127.                 {
  1128.                     // what should we assume here about automatic encoding of datetimes
  1129.                     // and php classes instances???
  1130.                     $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates')));
  1131.                 }
  1132.             }
  1133.             if($this->debug > 2)
  1134.             {
  1135.                 // note: restore the error handler we found before calling the
  1136.                 // user func, even if it has been changed inside the func itself
  1137.                 if($GLOBALS['_xmlrpcs_prev_ehandler'])
  1138.                 {
  1139.                     set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
  1140.                 }
  1141.                 else
  1142.                 {
  1143.                     restore_error_handler();
  1144.                 }
  1145.             }
  1146.             return $r;
  1147.         }
  1148.  
  1149.         /**
  1150.         * add a string to the 'internal debug message' (separate from 'user debug message')
  1151.         * @param string $strings
  1152.         * @access private
  1153.         */
  1154.         function debugmsg($string)
  1155.         {
  1156.             $this->debug_info .= $string."\n";
  1157.         }
  1158.  
  1159.         /**
  1160.         * @access private
  1161.         */
  1162.         function xml_header($charset_encoding='')
  1163.         {
  1164.             if ($charset_encoding != '')
  1165.             {
  1166.                 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
  1167.             }
  1168.             else
  1169.             {
  1170.                 return "<?xml version=\"1.0\"?" . ">\n";
  1171.             }
  1172.         }
  1173.  
  1174.         /**
  1175.         * A debugging routine: just echoes back the input packet as a string value
  1176.         * DEPRECATED!
  1177.         */
  1178.         function echoInput()
  1179.         {
  1180.             $r=&new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
  1181.             print $r->serialize();
  1182.         }
  1183.     }
  1184. ?>